Micron Document
🎖️GitЯра🎖️

Node / meshtastic / Meshtastic-Android / files / core / database / src / commonTest / kotlin / org / meshtastic / core / database / DatabaseManagerShutdownTest.kt

Displaying Raw • Download

core/database/src/commonTest/kotlin/org/meshtastic/core/database/DatabaseManagerShutdownTest.kt d5848ad5e02bd9b5b1726bedfa51ceb0faaa0240 (d5848ad5) Text, 24.02 KB

T8b949e/*
* Copyright (c) 2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
Tff7b72package T7ee787org.meshtastic.core.database

Tff7b72import T7ee787kotlinx.coroutines.CompletableDeferred
Tff7b72import T7ee787kotlinx.coroutines.Dispatchers
Tff7b72import T7ee787kotlinx.coroutines.NonCancellable
Tff7b72import T7ee787kotlinx.coroutines.async
Tff7b72import T7ee787kotlinx.coroutines.flow.first
Tff7b72import T7ee787kotlinx.coroutines.flow.flow
Tff7b72import T7ee787kotlinx.coroutines.flow.flowOf
Tff7b72import T7ee787kotlinx.coroutines.launch
Tff7b72import T7ee787kotlinx.coroutines.test.StandardTestDispatcher
Tff7b72import T7ee787kotlinx.coroutines.test.advanceTimeBy
Tff7b72import T7ee787kotlinx.coroutines.test.runCurrent
Tff7b72import T7ee787kotlinx.coroutines.test.runTest
Tff7b72import T7ee787kotlinx.coroutines.withContext
Tff7b72import T7ee787kotlinx.coroutines.yield
Tff7b72import T7ee787kotlin.test.AfterTest
Tff7b72import T7ee787kotlin.test.BeforeTest
Tff7b72import T7ee787kotlin.test.Test
Tff7b72import T7ee787kotlin.test.assertEquals
Tff7b72import T7ee787kotlin.test.assertFailsWith
Tff7b72import T7ee787kotlin.test.assertFalse
Tff7b72import T7ee787kotlin.test.assertNotNull
Tff7b72import T7ee787kotlin.test.assertNull
Tff7b72import T7ee787kotlin.test.assertTrue

Tff7b72class T56d364DatabaseManagerShutdownTest Tb4b4b4: Te6edf3DatabaseManagerTestFixtureTb4b4b4(Tb4b4b4) Tb4b4b4{

Tf0883e@BeforeTest Tff7b72fun Td2a8ffsetUpTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3setUpFixtureTb4b4b4(Tb4b4b4)

Tf0883e@AfterTest Tff7b72fun Td2a8fftearDownTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3tearDownFixtureTb4b4b4(Tb4b4b4)

Tf0883e@Test
Tff7b72fun Td2a8ffcloseWaitsForAdmittedWriterBeforeClosingPoolsTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTestTb4b4b4(Te6edf3testDispatcherTb4b4b4) Tb4b4b4{
Te6edf3managerTb4b4b4.Te6edf3switchActiveDatabaseTb4b4b4(Ta5d6ff"Ta5d6ffaddrATa5d6ff"Tb4b4b4)
Tff7b72val Te6edf3writerStarted Tff7b72= Te6edf3CompletableDeferredTff7b72<Tffa657UnitTff7b72>Tb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3releaseWriter Tff7b72= Te6edf3CompletableDeferredTff7b72<Tffa657UnitTff7b72>Tb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3writerJob Tff7b72= Te6edf3launch Tb4b4b4{
Te6edf3managerTb4b4b4.Te6edf3withDb Tb4b4b4{
Te6edf3writerStartedTb4b4b4.Te6edf3completeTb4b4b4(Tffa657UnitTb4b4b4)
Te6edf3releaseWriterTb4b4b4.Te6edf3awaitTb4b4b4(Tb4b4b4)
Tff7b72null
Tb4b4b4}
Tb4b4b4}
Te6edf3writerStartedTb4b4b4.Te6edf3awaitTb4b4b4(Tb4b4b4)

Tff7b72val Te6edf3closeJob Tff7b72= Te6edf3launch Tb4b4b4{ Te6edf3managerTb4b4b4.Te6edf3closeTb4b4b4(Tb4b4b4) Tb4b4b4}
Te6edf3runCurrentTb4b4b4(Tb4b4b4)

Te6edf3assertFalseTb4b4b4(Te6edf3closeJobTb4b4b4.Te6edf3isCompletedTb4b4b4, Ta5d6ff"Ta5d6ffclose must wait for the admitted writerTa5d6ff"Tb4b4b4)
Te6edf3assertTrueTb4b4b4(Te6edf3managerTb4b4b4.Te6edf3closedDatabasesTb4b4b4.Te6edf3isEmptyTb4b4b4(Tb4b4b4)Tb4b4b4, Ta5d6ff"Ta5d6ffno pool may close while an admitted writer is activeTa5d6ff"Tb4b4b4)
Te6edf3releaseWriterTb4b4b4.Te6edf3completeTb4b4b4(Tffa657UnitTb4b4b4)
Te6edf3writerJobTb4b4b4.Te6edf3joinTb4b4b4(Tb4b4b4)
Te6edf3closeJobTb4b4b4.Te6edf3joinTb4b4b4(Tb4b4b4)

Te6edf3managerTb4b4b4.Te6edf3builtDatabasesTb4b4b4.Te6edf3forEach Tb4b4b4{ Te6edf3database Tff7b72-Tff7b72>
Te6edf3assertEqualsTb4b4b4(T79c0ff1Tb4b4b4, Te6edf3managerTb4b4b4.Te6edf3closedDatabasesTb4b4b4.Te6edf3count Tb4b4b4{ Tffa657it Tff7b72=Tff7b72=Tff7b72= Te6edf3database Tb4b4b4}Tb4b4b4)
Tb4b4b4}
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ffcloseBoundsWedgedWriterDrainWithoutClosingReachablePoolsTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTestTb4b4b4(Te6edf3testDispatcherTb4b4b4) Tb4b4b4{
Te6edf3managerTb4b4b4.Te6edf3switchActiveDatabaseTb4b4b4(Ta5d6ff"Ta5d6ffaddrATa5d6ff"Tb4b4b4)
Tff7b72val Te6edf3writerStarted Tff7b72= Te6edf3CompletableDeferredTff7b72<Tffa657UnitTff7b72>Tb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3releaseWriter Tff7b72= Te6edf3CompletableDeferredTff7b72<Tffa657UnitTff7b72>Tb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3writerJob Tff7b72= Te6edf3launch Tb4b4b4{
Te6edf3managerTb4b4b4.Te6edf3withDb Tb4b4b4{
Te6edf3writerStartedTb4b4b4.Te6edf3completeTb4b4b4(Tffa657UnitTb4b4b4)
Te6edf3releaseWriterTb4b4b4.Te6edf3awaitTb4b4b4(Tb4b4b4)
Tff7b72null
Tb4b4b4}
Tb4b4b4}
Te6edf3writerStartedTb4b4b4.Te6edf3awaitTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3closeJob Tff7b72= Te6edf3launch Tb4b4b4{ Te6edf3managerTb4b4b4.Te6edf3closeTb4b4b4(Tb4b4b4) Tb4b4b4}
Te6edf3runCurrentTb4b4b4(Tb4b4b4)
Te6edf3assertFalseTb4b4b4(Te6edf3closeJobTb4b4b4.Te6edf3isCompletedTb4b4b4)

Te6edf3advanceTimeByTb4b4b4(T79c0ff5Te6edf3_501Tb4b4b4)
Te6edf3runCurrentTb4b4b4(Tb4b4b4)

Te6edf3assertTrueTb4b4b4(Te6edf3closeJobTb4b4b4.Te6edf3isCompletedTb4b4b4, Ta5d6ff"Ta5d6ffshutdown must return after the bounded writer-drain timeoutTa5d6ff"Tb4b4b4)
Te6edf3assertTrueTb4b4b4(Te6edf3managerTb4b4b4.Te6edf3closedDatabasesTb4b4b4.Te6edf3isEmptyTb4b4b4(Tb4b4b4)Tb4b4b4, Ta5d6ff"Ta5d6ffa writer may still resume, so no pool is safe to closeTa5d6ff"Tb4b4b4)
Te6edf3assertTrueTb4b4b4(Te6edf3managerTb4b4b4.Te6edf3deletedDatabaseNamesTb4b4b4.Te6edf3isEmptyTb4b4b4(Tb4b4b4)Tb4b4b4, Ta5d6ff"Ta5d6ffno reachable database file may be retiredTa5d6ff"Tb4b4b4)
Te6edf3assertFalseTb4b4b4(Te6edf3managerTb4b4b4.Te6edf3debugAcceptingWritesTb4b4b4(Tb4b4b4)Tb4b4b4)

Te6edf3releaseWriterTb4b4b4.Te6edf3completeTb4b4b4(Tffa657UnitTb4b4b4)
Te6edf3writerJobTb4b4b4.Te6edf3joinTb4b4b4(Tb4b4b4)
Te6edf3managerTb4b4b4.Te6edf3closeTb4b4b4(Tb4b4b4)
Te6edf3managerTb4b4b4.Te6edf3builtDatabasesTb4b4b4.Te6edf3forEach Tb4b4b4{ Te6edf3database Tff7b72-Tff7b72>
Te6edf3assertEqualsTb4b4b4(T79c0ff1Tb4b4b4, Te6edf3managerTb4b4b4.Te6edf3closedDatabasesTb4b4b4.Te6edf3count Tb4b4b4{ Tffa657it Tff7b72=Tff7b72=Tff7b72= Te6edf3database Tb4b4b4}Tb4b4b4)
Tb4b4b4}
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ffcloseBoundsNoncancellableManagerJobWithoutClosingOwnedPoolsTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTestTb4b4b4(Te6edf3testDispatcherTb4b4b4) Tb4b4b4{
Te6edf3managerTb4b4b4.Te6edf3switchActiveDatabaseTb4b4b4(Ta5d6ff"Ta5d6ffaddrATa5d6ff"Tb4b4b4)
Tff7b72val Te6edf3workStarted Tff7b72= Te6edf3CompletableDeferredTff7b72<Tffa657UnitTff7b72>Tb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3releaseWork Tff7b72= Te6edf3CompletableDeferredTff7b72<Tffa657UnitTff7b72>Tb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3blockedWork Tff7b72=
Te6edf3managerTb4b4b4.Te6edf3launchManagerWorkForTest Tb4b4b4{
Te6edf3workStartedTb4b4b4.Te6edf3completeTb4b4b4(Tffa657UnitTb4b4b4)
Te6edf3withContextTb4b4b4(Te6edf3NonCancellableTb4b4b4) Tb4b4b4{ Te6edf3releaseWorkTb4b4b4.Te6edf3awaitTb4b4b4(Tb4b4b4) Tb4b4b4}
Tb4b4b4}
Te6edf3workStartedTb4b4b4.Te6edf3awaitTb4b4b4(Tb4b4b4)

Tff7b72val Te6edf3closeJob Tff7b72= Te6edf3launch Tb4b4b4{ Te6edf3managerTb4b4b4.Te6edf3closeTb4b4b4(Tb4b4b4) Tb4b4b4}
Te6edf3runCurrentTb4b4b4(Tb4b4b4)
Te6edf3assertFalseTb4b4b4(Te6edf3closeJobTb4b4b4.Te6edf3isCompletedTb4b4b4)
Te6edf3advanceTimeByTb4b4b4(T79c0ff5Te6edf3_501Tb4b4b4)
Te6edf3runCurrentTb4b4b4(Tb4b4b4)

Te6edf3assertTrueTb4b4b4(Te6edf3closeJobTb4b4b4.Te6edf3isCompletedTb4b4b4, Ta5d6ff"Ta5d6ffshutdown must return after its manager-job cancellation boundTa5d6ff"Tb4b4b4)
Te6edf3assertFalseTb4b4b4(Te6edf3managerTb4b4b4.Te6edf3debugAcceptingWritesTb4b4b4(Tb4b4b4)Tb4b4b4, Ta5d6ff"Ta5d6ffshutdown must leave a terminal admission boundaryTa5d6ff"Tb4b4b4)
Te6edf3assertTrueTb4b4b4(Te6edf3managerTb4b4b4.Te6edf3closedDatabasesTb4b4b4.Te6edf3isEmptyTb4b4b4(Tb4b4b4)Tb4b4b4, Ta5d6ff"Ta5d6ffthe blocked manager job may still reach its databaseTa5d6ff"Tb4b4b4)
Te6edf3assertTrueTb4b4b4(Te6edf3managerTb4b4b4.Te6edf3deletedDatabaseNamesTb4b4b4.Te6edf3isEmptyTb4b4b4(Tb4b4b4)Tb4b4b4, Ta5d6ff"Ta5d6ffphysical retirement must be skipped with live workTa5d6ff"Tb4b4b4)
Te6edf3managerTb4b4b4.Te6edf3closeTb4b4b4(Tb4b4b4)
Te6edf3assertTrueTb4b4b4(
Te6edf3managerTb4b4b4.Te6edf3closedDatabasesTb4b4b4.Te6edf3isEmptyTb4b4b4(Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffretry must remain bounded while the manager job is still liveTa5d6ff"Tb4b4b4,
Tb4b4b4)

Te6edf3releaseWorkTb4b4b4.Te6edf3completeTb4b4b4(Tffa657UnitTb4b4b4)
Te6edf3blockedWorkTb4b4b4.Te6edf3joinTb4b4b4(Tb4b4b4)
Te6edf3managerTb4b4b4.Te6edf3closeTb4b4b4(Tb4b4b4)
Te6edf3managerTb4b4b4.Te6edf3builtDatabasesTb4b4b4.Te6edf3forEach Tb4b4b4{ Te6edf3database Tff7b72-Tff7b72>
Te6edf3assertEqualsTb4b4b4(T79c0ff1Tb4b4b4, Te6edf3managerTb4b4b4.Te6edf3closedDatabasesTb4b4b4.Te6edf3count Tb4b4b4{ Tffa657it Tff7b72=Tff7b72=Tff7b72= Te6edf3database Tb4b4b4}Tb4b4b4)
Tb4b4b4}
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ffcloseUnregistersAcceptedLazyManagerJobCancelledBeforeBodyStartsTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTestTb4b4b4(Te6edf3testDispatcherTb4b4b4) Tb4b4b4{
Te6edf3managerTb4b4b4.Te6edf3switchActiveDatabaseTb4b4b4(Ta5d6ff"Ta5d6ffaddrATa5d6ff"Tb4b4b4)
Tff7b72val Te6edf3queuedDispatcher Tff7b72= Te6edf3StandardTestDispatcherTb4b4b4(Te6edf3testSchedulerTb4b4b4)
Tff7b72var Te6edf3bodyStarted Tff7b72= Tff7b72false
Tff7b72val Te6edf3queuedJob Tff7b72= Te6edf3managerTb4b4b4.Te6edf3launchManagerWorkForTestTb4b4b4(Te6edf3queuedDispatcherTb4b4b4) Tb4b4b4{ Te6edf3bodyStarted Tff7b72= Tff7b72true Tb4b4b4}

Te6edf3assertFalseTb4b4b4(Te6edf3bodyStartedTb4b4b4, Ta5d6ff"Ta5d6ffthe queued dispatcher must hold the LAZY job before its body startsTa5d6ff"Tb4b4b4)

Te6edf3managerTb4b4b4.Te6edf3closeTb4b4b4(Tb4b4b4)

Te6edf3assertTrueTb4b4b4(Te6edf3queuedJobTb4b4b4.Te6edf3isCancelledTb4b4b4, Ta5d6ff"Ta5d6ffshutdown must cancel the accepted manager jobTa5d6ff"Tb4b4b4)
Te6edf3assertFalseTb4b4b4(Te6edf3bodyStartedTb4b4b4, Ta5d6ff"Ta5d6ffcancelling before dispatch must not execute the job bodyTa5d6ff"Tb4b4b4)
Te6edf3assertTrueTb4b4b4(
Te6edf3managerTb4b4b4.Te6edf3closedDatabasesTb4b4b4.Te6edf3isNotEmptyTb4b4b4(Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffcompletion-based unregistration must let shutdown reclaim its database pools without timing outTa5d6ff"Tb4b4b4,
Tb4b4b4)
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ffclosingRejectsNewWritersSwitchesAndAssociationsTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTestTb4b4b4(Te6edf3testDispatcherTb4b4b4) Tb4b4b4{
Te6edf3managerTb4b4b4.Te6edf3switchActiveDatabaseTb4b4b4(Ta5d6ff"Ta5d6ffaddrATa5d6ff"Tb4b4b4)
Tff7b72val Te6edf3writerStarted Tff7b72= Te6edf3CompletableDeferredTff7b72<Tffa657UnitTff7b72>Tb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3releaseWriter Tff7b72= Te6edf3CompletableDeferredTff7b72<Tffa657UnitTff7b72>Tb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3admittedWriter Tff7b72= Te6edf3launch Tb4b4b4{
Te6edf3managerTb4b4b4.Te6edf3withDb Tb4b4b4{
Te6edf3writerStartedTb4b4b4.Te6edf3completeTb4b4b4(Tffa657UnitTb4b4b4)
Te6edf3releaseWriterTb4b4b4.Te6edf3awaitTb4b4b4(Tb4b4b4)
Tff7b72null
Tb4b4b4}
Tb4b4b4}
Te6edf3writerStartedTb4b4b4.Te6edf3awaitTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3closeJob Tff7b72= Te6edf3launch Tb4b4b4{ Te6edf3managerTb4b4b4.Te6edf3closeTb4b4b4(Tb4b4b4) Tb4b4b4}
Te6edf3runCurrentTb4b4b4(Tb4b4b4)
Te6edf3assertFalseTb4b4b4(Te6edf3managerTb4b4b4.Te6edf3debugAcceptingWritesTb4b4b4(Tb4b4b4)Tb4b4b4)

Tff7b72var Te6edf3rejectedWriterRan Tff7b72= Tff7b72false
Te6edf3assertFailsWithTff7b72<Te6edf3IllegalStateExceptionTff7b72> Tb4b4b4{
Te6edf3managerTb4b4b4.Te6edf3withDb Tb4b4b4{
Te6edf3rejectedWriterRan Tff7b72= Tff7b72true
Tff7b72null
Tb4b4b4}
Tb4b4b4}
Te6edf3assertFalseTb4b4b4(Te6edf3rejectedWriterRanTb4b4b4)
Te6edf3assertFailsWithTff7b72<Te6edf3IllegalStateExceptionTff7b72> Tb4b4b4{ Te6edf3managerTb4b4b4.Te6edf3switchActiveDatabaseTb4b4b4(Ta5d6ff"Ta5d6ffaddrBTa5d6ff"Tb4b4b4) Tb4b4b4}
Te6edf3assertFailsWithTff7b72<Te6edf3IllegalStateExceptionTff7b72> Tb4b4b4{
Te6edf3managerTb4b4b4.Te6edf3associateCurrentDeviceTb4b4b4(Te6edf3nodeNum Tff7b72= T79c0ff1T79c0ff2T79c0ff3Tb4b4b4, Te6edf3deviceId Tff7b72= Ta5d6ff"Ta5d6ffdeadbeefdeadbeefTa5d6ff"Tb4b4b4)
Tb4b4b4}

Te6edf3releaseWriterTb4b4b4.Te6edf3completeTb4b4b4(Tffa657UnitTb4b4b4)
Te6edf3admittedWriterTb4b4b4.Te6edf3joinTb4b4b4(Tb4b4b4)
Te6edf3closeJobTb4b4b4.Te6edf3joinTb4b4b4(Tb4b4b4)
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ffconcurrentCloseCallsCleanEachPoolOnceTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTestTb4b4b4(Te6edf3testDispatcherTb4b4b4) Tb4b4b4{
Te6edf3managerTb4b4b4.Te6edf3switchActiveDatabaseTb4b4b4(Ta5d6ff"Ta5d6ffaddrATa5d6ff"Tb4b4b4)
Tff7b72val Te6edf3writerStarted Tff7b72= Te6edf3CompletableDeferredTff7b72<Tffa657UnitTff7b72>Tb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3releaseWriter Tff7b72= Te6edf3CompletableDeferredTff7b72<Tffa657UnitTff7b72>Tb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3admittedWriter Tff7b72= Te6edf3launch Tb4b4b4{
Te6edf3managerTb4b4b4.Te6edf3withDb Tb4b4b4{
Te6edf3writerStartedTb4b4b4.Te6edf3completeTb4b4b4(Tffa657UnitTb4b4b4)
Te6edf3releaseWriterTb4b4b4.Te6edf3awaitTb4b4b4(Tb4b4b4)
Tff7b72null
Tb4b4b4}
Tb4b4b4}
Te6edf3writerStartedTb4b4b4.Te6edf3awaitTb4b4b4(Tb4b4b4)

Tff7b72val Te6edf3firstClose Tff7b72= Te6edf3launch Tb4b4b4{ Te6edf3managerTb4b4b4.Te6edf3closeTb4b4b4(Tb4b4b4) Tb4b4b4}
Tff7b72val Te6edf3secondClose Tff7b72= Te6edf3launch Tb4b4b4{ Te6edf3managerTb4b4b4.Te6edf3closeTb4b4b4(Tb4b4b4) Tb4b4b4}
Te6edf3runCurrentTb4b4b4(Tb4b4b4)
Te6edf3assertFalseTb4b4b4(Te6edf3firstCloseTb4b4b4.Te6edf3isCompletedTb4b4b4)
Te6edf3assertFalseTb4b4b4(Te6edf3secondCloseTb4b4b4.Te6edf3isCompletedTb4b4b4)
Te6edf3releaseWriterTb4b4b4.Te6edf3completeTb4b4b4(Tffa657UnitTb4b4b4)
Te6edf3admittedWriterTb4b4b4.Te6edf3joinTb4b4b4(Tb4b4b4)
Te6edf3firstCloseTb4b4b4.Te6edf3joinTb4b4b4(Tb4b4b4)
Te6edf3secondCloseTb4b4b4.Te6edf3joinTb4b4b4(Tb4b4b4)

Te6edf3managerTb4b4b4.Te6edf3builtDatabasesTb4b4b4.Te6edf3forEach Tb4b4b4{ Te6edf3database Tff7b72-Tff7b72>
Te6edf3assertEqualsTb4b4b4(T79c0ff1Tb4b4b4, Te6edf3managerTb4b4b4.Te6edf3closedDatabasesTb4b4b4.Te6edf3count Tb4b4b4{ Tffa657it Tff7b72=Tff7b72=Tff7b72= Te6edf3database Tb4b4b4}Tb4b4b4)
Tb4b4b4}
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8fftimeoutRecoveryCannotReopenWhileClosingTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTestTb4b4b4(Te6edf3testDispatcherTb4b4b4) Tb4b4b4{
Te6edf3managerTb4b4b4.Te6edf3switchActiveDatabaseTb4b4b4(Ta5d6ff"Ta5d6ffaddrATa5d6ff"Tb4b4b4)
Tff7b72val Te6edf3callbackStarted Tff7b72= Te6edf3CompletableDeferredTff7b72<Tffa657UnitTff7b72>Tb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3releaseFailure Tff7b72= Te6edf3CompletableDeferredTff7b72<Tffa657UnitTff7b72>Tb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3writerJob Tff7b72= Te6edf3async Tb4b4b4{
Tff7b72try Tb4b4b4{
Te6edf3managerTb4b4b4.Te6edf3withDbTff7b72<Tffa657UnitTff7b72> Tb4b4b4{
Te6edf3callbackStartedTb4b4b4.Te6edf3completeTb4b4b4(Tffa657UnitTb4b4b4)
Te6edf3releaseFailureTb4b4b4.Te6edf3awaitTb4b4b4(Tb4b4b4)
Tff7b72throw Te6edf3roomPoolTimeoutTb4b4b4(Tb4b4b4)
Tb4b4b4}
Tff7b72null
Tb4b4b4} Tff7b72catch Tb4b4b4(Te6edf3failureTb4b4b4: Te6edf3IllegalStateExceptionTb4b4b4) Tb4b4b4{
Te6edf3failure
Tb4b4b4}
Tb4b4b4}
Te6edf3callbackStartedTb4b4b4.Te6edf3awaitTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3buildsBeforeClose Tff7b72= Te6edf3managerTb4b4b4.Te6edf3builtDatabasesTb4b4b4.Te6edf3size
Tff7b72val Te6edf3closeJob Tff7b72= Te6edf3launch Tb4b4b4{ Te6edf3managerTb4b4b4.Te6edf3closeTb4b4b4(Tb4b4b4) Tb4b4b4}
Te6edf3runCurrentTb4b4b4(Tb4b4b4)
Te6edf3assertFalseTb4b4b4(Te6edf3managerTb4b4b4.Te6edf3debugAcceptingWritesTb4b4b4(Tb4b4b4)Tb4b4b4)

Te6edf3releaseFailureTb4b4b4.Te6edf3completeTb4b4b4(Tffa657UnitTb4b4b4)
Tff7b72val Te6edf3failure Tff7b72= Te6edf3writerJobTb4b4b4.Te6edf3awaitTb4b4b4(Tb4b4b4)
Te6edf3closeJobTb4b4b4.Te6edf3joinTb4b4b4(Tb4b4b4)

Te6edf3assertNotNullTb4b4b4(Te6edf3failureTb4b4b4)
Te6edf3assertTrueTb4b4b4(Te6edf3failureTb4b4b4.Te6edf3messageTb4b4b4.Te6edf3orEmptyTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3containsTb4b4b4(Ta5d6ff"Ta5d6ffTimed out attempting to acquireTa5d6ff"Tb4b4b4)Tb4b4b4)
Te6edf3assertEqualsTb4b4b4(
Te6edf3buildsBeforeCloseTb4b4b4,
Te6edf3managerTb4b4b4.Te6edf3builtDatabasesTb4b4b4.Te6edf3sizeTb4b4b4,
Ta5d6ff"Ta5d6ffclosing must suppress timeout recovery reopenTa5d6ff"Tb4b4b4,
Tb4b4b4)
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ffcloseBeforeCurrentDbInitializationDoesNotBuildDefaultPoolTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTestTb4b4b4(Te6edf3testDispatcherTb4b4b4) Tb4b4b4{
Te6edf3assertTrueTb4b4b4(Te6edf3managerTb4b4b4.Te6edf3builtDatabasesTb4b4b4.Te6edf3isEmptyTb4b4b4(Tb4b4b4)Tb4b4b4)

Te6edf3managerTb4b4b4.Te6edf3closeTb4b4b4(Tb4b4b4)

Te6edf3assertTrueTb4b4b4(Te6edf3managerTb4b4b4.Te6edf3builtDatabasesTb4b4b4.Te6edf3isEmptyTb4b4b4(Tb4b4b4)Tb4b4b4)
Te6edf3assertFailsWithTff7b72<Te6edf3IllegalStateExceptionTff7b72> Tb4b4b4{ Te6edf3managerTb4b4b4.Te6edf3currentDbTb4b4b4.Te6edf3value Tb4b4b4}
Te6edf3assertTrueTb4b4b4(Te6edf3managerTb4b4b4.Te6edf3builtDatabasesTb4b4b4.Te6edf3isEmptyTb4b4b4(Tb4b4b4)Tb4b4b4, Ta5d6ff"Ta5d6fffirst access after close must not build the default poolTa5d6ff"Tb4b4b4)
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ffshutdownCannotMissDefaultPoolInitializationBeforePublicationTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTestTb4b4b4(Te6edf3testDispatcherTb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3buildStarted Tff7b72= Te6edf3CompletableDeferredTff7b72<Tffa657UnitTff7b72>Tb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3releaseBuild Tff7b72= Te6edf3CompletableDeferredTff7b72<Tffa657UnitTff7b72>Tb4b4b4(Tb4b4b4)
Te6edf3managerTb4b4b4.Te6edf3defaultBuildStarted Tff7b72= Te6edf3buildStarted
Te6edf3managerTb4b4b4.Te6edf3releaseDefaultBuild Tff7b72= Te6edf3releaseBuild

Tff7b72val Te6edf3initialization Tff7b72=
Te6edf3asyncTb4b4b4(Te6edf3DispatchersTb4b4b4.Te6edf3DefaultTb4b4b4) Tb4b4b4{
Tff7b72try Tb4b4b4{
Te6edf3managerTb4b4b4.Te6edf3currentDbTb4b4b4.Te6edf3value
Tff7b72null
Tb4b4b4} Tff7b72catch Tb4b4b4(Te6edf3failureTb4b4b4: Te6edf3IllegalStateExceptionTb4b4b4) Tb4b4b4{
Te6edf3failure
Tb4b4b4}
Tb4b4b4}
Te6edf3buildStartedTb4b4b4.Te6edf3awaitTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3closeJob Tff7b72= Te6edf3asyncTb4b4b4(Te6edf3DispatchersTb4b4b4.Te6edf3DefaultTb4b4b4) Tb4b4b4{ Te6edf3managerTb4b4b4.Te6edf3closeTb4b4b4(Tb4b4b4) Tb4b4b4}
Tff7b72while Tb4b4b4(Te6edf3managerTb4b4b4.Te6edf3debugAcceptingWritesTb4b4b4(Tb4b4b4)Tb4b4b4) Te6edf3yieldTb4b4b4(Tb4b4b4)

Te6edf3releaseBuildTb4b4b4.Te6edf3completeTb4b4b4(Tffa657UnitTb4b4b4)

Te6edf3assertNotNullTb4b4b4(Te6edf3initializationTb4b4b4.Te6edf3awaitTb4b4b4(Tb4b4b4)Tb4b4b4, Ta5d6ff"Ta5d6ffinitialization racing shutdown must not publish the default poolTa5d6ff"Tb4b4b4)
Te6edf3closeJobTb4b4b4.Te6edf3awaitTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3defaultDatabase Tff7b72= Te6edf3managerTb4b4b4.Te6edf3builtDatabasesTb4b4b4.Te6edf3singleTb4b4b4(Tb4b4b4)
Te6edf3assertEqualsTb4b4b4(T79c0ff1Tb4b4b4, Te6edf3managerTb4b4b4.Te6edf3closedDatabasesTb4b4b4.Te6edf3count Tb4b4b4{ Tffa657it Tff7b72=Tff7b72=Tff7b72= Te6edf3defaultDatabase Tb4b4b4}Tb4b4b4)
Te6edf3assertFailsWithTff7b72<Te6edf3IllegalStateExceptionTff7b72> Tb4b4b4{ Te6edf3managerTb4b4b4.Te6edf3currentDbTb4b4b4.Te6edf3value Tb4b4b4}
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8fftimeoutRecoveryReopensTheLazyDefaultPoolTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTestTb4b4b4(Te6edf3testDispatcherTb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3original Tff7b72= Te6edf3managerTb4b4b4.Te6edf3currentDbTb4b4b4.Te6edf3value
Te6edf3managerTb4b4b4.Te6edf3switchActiveDatabaseTb4b4b4(Ta5d6ff"Ta5d6ffaddrATa5d6ff"Tb4b4b4)
Te6edf3managerTb4b4b4.Te6edf3switchActiveDatabaseTb4b4b4(Tff7b72nullTb4b4b4)
Te6edf3assertTrueTb4b4b4(Te6edf3managerTb4b4b4.Te6edf3currentDbTb4b4b4.Te6edf3value Tff7b72=Tff7b72=Tff7b72= Te6edf3originalTb4b4b4, Ta5d6ff"Ta5d6ffthe default pool should be cached before recoveryTa5d6ff"Tb4b4b4)
Tff7b72var Te6edf3invocationCount Tff7b72= T79c0ff0

Tff7b72val Te6edf3result Tff7b72= Te6edf3runCatching Tb4b4b4{
Te6edf3managerTb4b4b4.Te6edf3withDbTff7b72<Tffa657UnitTff7b72> Tb4b4b4{
Te6edf3invocationCount Tff7b72+Tff7b72= T79c0ff1
Tff7b72throw Te6edf3roomPoolTimeoutTb4b4b4(Tb4b4b4)
Tb4b4b4}
Tb4b4b4}

Te6edf3assertNotNullTb4b4b4(Te6edf3resultTb4b4b4.Te6edf3exceptionOrNullTb4b4b4(Tb4b4b4)Tb4b4b4)
Te6edf3assertEqualsTb4b4b4(T79c0ff1Tb4b4b4, Te6edf3invocationCountTb4b4b4, Ta5d6ff"Ta5d6ffdefault-pool recovery must not replay the callbackTa5d6ff"Tb4b4b4)
Tff7b72val Te6edf3reopened Tff7b72= Te6edf3managerTb4b4b4.Te6edf3currentDbTb4b4b4.Te6edf3value
Te6edf3assertTrueTb4b4b4(Te6edf3reopened Tff7b72!Tff7b72=Tff7b72= Te6edf3originalTb4b4b4, Ta5d6ff"Ta5d6ffthe lazily registered default pool should be replaceableTa5d6ff"Tb4b4b4)

Tff7b72var Te6edf3laterCallDbTb4b4b4: Te6edf3MeshtasticDatabase? Tff7b72= Tff7b72null
Te6edf3managerTb4b4b4.Te6edf3withDbTff7b72<Tffa657UnitTff7b72> Tb4b4b4{ Te6edf3database Tff7b72-Tff7b72> Te6edf3laterCallDb Tff7b72= Te6edf3database Tb4b4b4}
Te6edf3assertTrueTb4b4b4(Te6edf3laterCallDb Tff7b72=Tff7b72=Tff7b72= Te6edf3reopenedTb4b4b4)

Te6edf3managerTb4b4b4.Te6edf3switchActiveDatabaseTb4b4b4(Ta5d6ff"Ta5d6ffaddrATa5d6ff"Tb4b4b4)
Te6edf3managerTb4b4b4.Te6edf3switchActiveDatabaseTb4b4b4(Tff7b72nullTb4b4b4)
Te6edf3assertTrueTb4b4b4(
Te6edf3managerTb4b4b4.Te6edf3currentDbTb4b4b4.Te6edf3value Tff7b72=Tff7b72=Tff7b72= Te6edf3reopenedTb4b4b4,
Ta5d6ff"Ta5d6ffswitching back to default must not republish the failed poolTa5d6ff"Tb4b4b4,
Tb4b4b4)

Te6edf3managerTb4b4b4.Te6edf3closeTb4b4b4(Tb4b4b4)
Te6edf3assertEqualsTb4b4b4(T79c0ff1Tb4b4b4, Te6edf3managerTb4b4b4.Te6edf3closedDatabasesTb4b4b4.Te6edf3count Tb4b4b4{ Tffa657it Tff7b72=Tff7b72=Tff7b72= Te6edf3original Tb4b4b4}Tb4b4b4)
Te6edf3assertEqualsTb4b4b4(T79c0ff1Tb4b4b4, Te6edf3managerTb4b4b4.Te6edf3closedDatabasesTb4b4b4.Te6edf3count Tb4b4b4{ Tffa657it Tff7b72=Tff7b72=Tff7b72= Te6edf3reopened Tb4b4b4}Tb4b4b4)
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ffflowPoolTimeoutReopensAndRelatchesWithoutRepeatingTheFailedPoolTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTestTb4b4b4(Te6edf3testDispatcherTb4b4b4) Tb4b4b4{
Te6edf3managerTb4b4b4.Te6edf3switchActiveDatabaseTb4b4b4(Ta5d6ff"Ta5d6ffaddrATa5d6ff"Tb4b4b4)
Tff7b72val Te6edf3original Tff7b72= Te6edf3managerTb4b4b4.Te6edf3currentDbTb4b4b4.Te6edf3value
Tff7b72var Te6edf3originalQueries Tff7b72= T79c0ff0

Tff7b72val Te6edf3value Tff7b72=
Te6edf3manager
Tb4b4b4.Te6edf3observeCurrentDb Tb4b4b4{ Te6edf3database Tff7b72-Tff7b72>
Tff7b72if Tb4b4b4(Te6edf3database Tff7b72=Tff7b72=Tff7b72= Te6edf3originalTb4b4b4) Tb4b4b4{
Te6edf3flowTff7b72<Tffa657StringTff7b72> Tb4b4b4{
Te6edf3originalQueries Tff7b72+Tff7b72= T79c0ff1
Tff7b72throw Te6edf3roomPoolTimeoutTb4b4b4(Tb4b4b4)
Tb4b4b4}
Tb4b4b4} Tff7b72else Tb4b4b4{
Te6edf3flowOfTb4b4b4(Ta5d6ff"Ta5d6ffrecoveredTa5d6ff"Tb4b4b4)
Tb4b4b4}
Tb4b4b4}
Tb4b4b4.Te6edf3firstTb4b4b4(Tb4b4b4)

Te6edf3assertEqualsTb4b4b4(Ta5d6ff"Ta5d6ffrecoveredTa5d6ff"Tb4b4b4, Te6edf3valueTb4b4b4)
Te6edf3assertEqualsTb4b4b4(T79c0ff1Tb4b4b4, Te6edf3originalQueriesTb4b4b4)
Te6edf3assertTrueTb4b4b4(Te6edf3managerTb4b4b4.Te6edf3currentDbTb4b4b4.Te6edf3value Tff7b72!Tff7b72=Tff7b72= Te6edf3originalTb4b4b4)
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ffflowPoolRecoveryStopsAfterIntermittentFailuresReachTheManagerLifetimeLimitTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTestTb4b4b4(Te6edf3testDispatcherTb4b4b4) Tb4b4b4{
Te6edf3managerTb4b4b4.Te6edf3switchActiveDatabaseTb4b4b4(Ta5d6ff"Ta5d6ffaddrATa5d6ff"Tb4b4b4)
Tff7b72val Te6edf3buildsBeforeRecovery Tff7b72= Te6edf3managerTb4b4b4.Te6edf3builtDatabasesTb4b4b4.Te6edf3size
Tff7b72var Te6edf3successfulEmissions Tff7b72= T79c0ff0

Tff7b72val Te6edf3failure Tff7b72=
Te6edf3assertFailsWithTff7b72<Te6edf3IllegalStateExceptionTff7b72> Tb4b4b4{
Te6edf3manager
Tb4b4b4.Te6edf3observeCurrentDb Tb4b4b4{
Te6edf3flow Tb4b4b4{
Te6edf3successfulEmissions Tff7b72+Tff7b72= T79c0ff1
Te6edf3emitTb4b4b4(Tffa657UnitTb4b4b4)
Tff7b72throw Te6edf3roomPoolTimeoutTb4b4b4(Tb4b4b4)
Tb4b4b4}
Tb4b4b4}
Tb4b4b4.Te6edf3first Tb4b4b4{ Tff7b72false Tb4b4b4}
Tb4b4b4}

Te6edf3assertTrueTb4b4b4(Te6edf3failureTb4b4b4.Te6edf3messageTb4b4b4.Te6edf3orEmptyTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3containsTb4b4b4(Ta5d6ff"Ta5d6ffTimed out attempting to acquireTa5d6ff"Tb4b4b4)Tb4b4b4)
Te6edf3assertEqualsTb4b4b4(Te6edf3MAX_FLOW_POOL_RECOVERIES_PER_MANAGER_LIFETIME Tff7b72+ T79c0ff1Tb4b4b4, Te6edf3successfulEmissionsTb4b4b4)
Te6edf3assertEqualsTb4b4b4(
Te6edf3buildsBeforeRecovery Tff7b72+ Te6edf3MAX_FLOW_POOL_RECOVERIES_PER_MANAGER_LIFETIMETb4b4b4,
Te6edf3managerTb4b4b4.Te6edf3builtDatabasesTb4b4b4.Te6edf3sizeTb4b4b4,
Ta5d6ff"Ta5d6ffsuccessful emissions must not reset the manager-lifetime Flow recovery budgetTa5d6ff"Tb4b4b4,
Tb4b4b4)
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ffflowPoolRecoveryStopsAfterConsecutiveReplacementPoolsFailTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTestTb4b4b4(Te6edf3testDispatcherTb4b4b4) Tb4b4b4{
Te6edf3managerTb4b4b4.Te6edf3switchActiveDatabaseTb4b4b4(Ta5d6ff"Ta5d6ffaddrATa5d6ff"Tb4b4b4)
Tff7b72val Te6edf3buildsBeforeRecovery Tff7b72= Te6edf3managerTb4b4b4.Te6edf3builtDatabasesTb4b4b4.Te6edf3size
Tff7b72var Te6edf3queryAttempts Tff7b72= T79c0ff0

Tff7b72val Te6edf3failure Tff7b72=
Te6edf3assertFailsWithTff7b72<Te6edf3IllegalStateExceptionTff7b72> Tb4b4b4{
Te6edf3manager
Tb4b4b4.Te6edf3observeCurrentDbTff7b72<Tffa657UnitTff7b72> Tb4b4b4{
Te6edf3flow Tb4b4b4{
Te6edf3queryAttempts Tff7b72+Tff7b72= T79c0ff1
Tff7b72throw Te6edf3roomPoolTimeoutTb4b4b4(Tb4b4b4)
Tb4b4b4}
Tb4b4b4}
Tb4b4b4.Te6edf3firstTb4b4b4(Tb4b4b4)
Tb4b4b4}

Te6edf3assertTrueTb4b4b4(Te6edf3failureTb4b4b4.Te6edf3messageTb4b4b4.Te6edf3orEmptyTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3containsTb4b4b4(Ta5d6ff"Ta5d6ffTimed out attempting to acquireTa5d6ff"Tb4b4b4)Tb4b4b4)
Te6edf3assertEqualsTb4b4b4(Te6edf3MAX_CONSECUTIVE_FLOW_POOL_RECOVERIES Tff7b72+ T79c0ff1Tb4b4b4, Te6edf3queryAttemptsTb4b4b4)
Te6edf3assertEqualsTb4b4b4(
Te6edf3buildsBeforeRecovery Tff7b72+ Te6edf3MAX_CONSECUTIVE_FLOW_POOL_RECOVERIESTb4b4b4,
Te6edf3managerTb4b4b4.Te6edf3builtDatabasesTb4b4b4.Te6edf3sizeTb4b4b4,
Ta5d6ff"Ta5d6ffautomatic recovery must bound the number of detached replacement poolsTa5d6ff"Tb4b4b4,
Tb4b4b4)
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ffflowPoolTimeoutRetainsItsOriginalFailureAfterShutdownClearsCurrentPublicationTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTestTb4b4b4(Te6edf3testDispatcherTb4b4b4) Tb4b4b4{
Te6edf3managerTb4b4b4.Te6edf3switchActiveDatabaseTb4b4b4(Ta5d6ff"Ta5d6ffaddrATa5d6ff"Tb4b4b4)
Tff7b72val Te6edf3buildsBeforeFailure Tff7b72= Te6edf3managerTb4b4b4.Te6edf3builtDatabasesTb4b4b4.Te6edf3size

Tff7b72val Te6edf3failure Tff7b72=
Te6edf3assertFailsWithTff7b72<Te6edf3IllegalStateExceptionTff7b72> Tb4b4b4{
Te6edf3manager
Tb4b4b4.Te6edf3observeCurrentDbTff7b72<Tffa657UnitTff7b72> Tb4b4b4{
Te6edf3flow Tb4b4b4{
Te6edf3managerTb4b4b4.Te6edf3closeTb4b4b4(Tb4b4b4)
Tff7b72throw Te6edf3roomPoolTimeoutTb4b4b4(Tb4b4b4)
Tb4b4b4}
Tb4b4b4}
Tb4b4b4.Te6edf3firstTb4b4b4(Tb4b4b4)
Tb4b4b4}

Te6edf3assertTrueTb4b4b4(Te6edf3failureTb4b4b4.Te6edf3messageTb4b4b4.Te6edf3orEmptyTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3containsTb4b4b4(Ta5d6ff"Ta5d6ffTimed out attempting to acquireTa5d6ff"Tb4b4b4)Tb4b4b4)
Te6edf3assertEqualsTb4b4b4(Te6edf3buildsBeforeFailureTb4b4b4, Te6edf3managerTb4b4b4.Te6edf3builtDatabasesTb4b4b4.Te6edf3sizeTb4b4b4, Ta5d6ff"Ta5d6ffshutdown must suppress Flow pool recoveryTa5d6ff"Tb4b4b4)
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ffflowRecoveryDoesNotSwallowUnrelatedDatabaseFailuresTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTestTb4b4b4(Te6edf3testDispatcherTb4b4b4) Tb4b4b4{
Te6edf3managerTb4b4b4.Te6edf3switchActiveDatabaseTb4b4b4(Ta5d6ff"Ta5d6ffaddrATa5d6ff"Tb4b4b4)
Tff7b72val Te6edf3original Tff7b72= Te6edf3managerTb4b4b4.Te6edf3currentDbTb4b4b4.Te6edf3value
Tff7b72val Te6edf3buildsBeforeFailure Tff7b72= Te6edf3managerTb4b4b4.Te6edf3builtDatabasesTb4b4b4.Te6edf3size

Tff7b72val Te6edf3failure Tff7b72=
Te6edf3assertFailsWithTff7b72<Te6edf3IllegalStateExceptionTff7b72> Tb4b4b4{
Te6edf3managerTb4b4b4.Te6edf3observeCurrentDbTff7b72<Tffa657StringTff7b72> Tb4b4b4{ Te6edf3flow Tb4b4b4{ Tff7b72throw Te6edf3IllegalStateExceptionTb4b4b4(Ta5d6ff"Ta5d6ffquery failedTa5d6ff"Tb4b4b4) Tb4b4b4} Tb4b4b4}Tb4b4b4.Te6edf3firstTb4b4b4(Tb4b4b4)
Tb4b4b4}

Te6edf3assertEqualsTb4b4b4(Ta5d6ff"Ta5d6ffquery failedTa5d6ff"Tb4b4b4, Te6edf3failureTb4b4b4.Te6edf3messageTb4b4b4)
Te6edf3assertTrueTb4b4b4(Te6edf3managerTb4b4b4.Te6edf3currentDbTb4b4b4.Te6edf3value Tff7b72=Tff7b72=Tff7b72= Te6edf3originalTb4b4b4)
Te6edf3assertEqualsTb4b4b4(
Te6edf3buildsBeforeFailureTb4b4b4,
Te6edf3managerTb4b4b4.Te6edf3builtDatabasesTb4b4b4.Te6edf3sizeTb4b4b4,
Ta5d6ff"Ta5d6ffunrelated failures must not trigger recoveryTa5d6ff"Tb4b4b4,
Tb4b4b4)
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8fftimeoutRecoveryFailureDoesNotMaskOriginalCallbackFailureTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTestTb4b4b4(Te6edf3testDispatcherTb4b4b4) Tb4b4b4{
Te6edf3managerTb4b4b4.Te6edf3switchActiveDatabaseTb4b4b4(Ta5d6ff"Ta5d6ffaddrATa5d6ff"Tb4b4b4)
Te6edf3managerTb4b4b4.Te6edf3failNextBuildWith Tff7b72= Te6edf3IllegalStateExceptionTb4b4b4(Ta5d6ff"Ta5d6ffforced reopen failureTa5d6ff"Tb4b4b4)

Tff7b72val Te6edf3failure Tff7b72= Te6edf3assertFailsWithTff7b72<Te6edf3IllegalStateExceptionTff7b72> Tb4b4b4{ Te6edf3managerTb4b4b4.Te6edf3withDbTff7b72<Tffa657UnitTff7b72> Tb4b4b4{ Tff7b72throw Te6edf3roomPoolTimeoutTb4b4b4(Tb4b4b4) Tb4b4b4} Tb4b4b4}

Te6edf3assertTrueTb4b4b4(Te6edf3failureTb4b4b4.Te6edf3messageTb4b4b4.Te6edf3orEmptyTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3containsTb4b4b4(Ta5d6ff"Ta5d6ffTimed out attempting to acquireTa5d6ff"Tb4b4b4)Tb4b4b4)
Te6edf3assertNullTb4b4b4(Te6edf3managerTb4b4b4.Te6edf3failNextBuildWithTb4b4b4, Ta5d6ff"Ta5d6ffthe reopen attempt should consume its injected failureTa5d6ff"Tb4b4b4)
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ffdetachedReopenedPoolIsClosedDuringOrderlyShutdownTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTestTb4b4b4(Te6edf3testDispatcherTb4b4b4) Tb4b4b4{
Te6edf3managerTb4b4b4.Te6edf3switchActiveDatabaseTb4b4b4(Ta5d6ff"Ta5d6ffaddrATa5d6ff"Tb4b4b4)
Tff7b72val Te6edf3original Tff7b72= Te6edf3managerTb4b4b4.Te6edf3currentDbTb4b4b4.Te6edf3value
Tff7b72var Te6edf3invocationCount Tff7b72= T79c0ff0

Tff7b72val Te6edf3firstResult Tff7b72= Te6edf3runCatching Tb4b4b4{
Te6edf3managerTb4b4b4.Te6edf3withDbTff7b72<Tffa657UnitTff7b72> Tb4b4b4{
Te6edf3invocationCount Tff7b72+Tff7b72= T79c0ff1
Tff7b72throw Te6edf3roomPoolTimeoutTb4b4b4(Tb4b4b4)
Tb4b4b4}
Tb4b4b4}

Te6edf3assertNotNullTb4b4b4(Te6edf3firstResultTb4b4b4.Te6edf3exceptionOrNullTb4b4b4(Tb4b4b4)Tb4b4b4)
Te6edf3assertEqualsTb4b4b4(T79c0ff1Tb4b4b4, Te6edf3invocationCountTb4b4b4, Ta5d6ff"Ta5d6ffpool recovery must not replay a callback that already startedTa5d6ff"Tb4b4b4)
Tff7b72val Te6edf3reopened Tff7b72= Te6edf3managerTb4b4b4.Te6edf3currentDbTb4b4b4.Te6edf3value
Te6edf3assertTrueTb4b4b4(Te6edf3reopened Tff7b72!Tff7b72=Tff7b72= Te6edf3originalTb4b4b4)
Te6edf3assertEqualsTb4b4b4(
T79c0ff0Tb4b4b4,
Te6edf3managerTb4b4b4.Te6edf3closedDatabasesTb4b4b4.Te6edf3count Tb4b4b4{ Tffa657it Tff7b72=Tff7b72=Tff7b72= Te6edf3original Tb4b4b4}Tb4b4b4,
Ta5d6ff"Ta5d6ffreopen must not close the published poolTa5d6ff"Tb4b4b4,
Tb4b4b4)

Tff7b72var Te6edf3laterCallDbTb4b4b4: Te6edf3MeshtasticDatabase? Tff7b72= Tff7b72null
Te6edf3managerTb4b4b4.Te6edf3withDbTff7b72<Tffa657UnitTff7b72> Tb4b4b4{ Te6edf3database Tff7b72-Tff7b72> Te6edf3laterCallDb Tff7b72= Te6edf3database Tb4b4b4}
Te6edf3assertTrueTb4b4b4(Te6edf3laterCallDb Tff7b72=Tff7b72=Tff7b72= Te6edf3reopenedTb4b4b4, Ta5d6ff"Ta5d6ffa later explicit call should use the recovered poolTa5d6ff"Tb4b4b4)

Te6edf3managerTb4b4b4.Te6edf3closeTb4b4b4(Tb4b4b4)

Te6edf3assertEqualsTb4b4b4(T79c0ff1Tb4b4b4, Te6edf3managerTb4b4b4.Te6edf3closedDatabasesTb4b4b4.Te6edf3count Tb4b4b4{ Tffa657it Tff7b72=Tff7b72=Tff7b72= Te6edf3original Tb4b4b4}Tb4b4b4)
Te6edf3assertEqualsTb4b4b4(T79c0ff1Tb4b4b4, Te6edf3managerTb4b4b4.Te6edf3closedDatabasesTb4b4b4.Te6edf3count Tb4b4b4{ Tffa657it Tff7b72=Tff7b72=Tff7b72= Te6edf3reopened Tb4b4b4}Tb4b4b4)
Tb4b4b4}

T8b949e/**
* Shutdown racing final routing waits for association ownership, then observes and physically removes the newly
* retired source exactly once. This covers the process-lifetime cleanup gap left by asynchronous retirement jobs.
*/
Tf0883e@Test
Tff7b72fun Td2a8ffshutdownRacingMergeFinalizationDeletesRetiredSourceExactlyOnceTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTestTb4b4b4(Te6edf3testDispatcherTb4b4b4) Tb4b4b4{
Te6edf3setupTwoDatabasesTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3mergeCommitted Tff7b72= Te6edf3CompletableDeferredTff7b72<Tffa657UnitTff7b72>Tb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3releaseFinalization Tff7b72= Te6edf3CompletableDeferredTff7b72<Tffa657UnitTff7b72>Tb4b4b4(Tb4b4b4)
Te6edf3managerTb4b4b4.Te6edf3afterMergeCommitted Tff7b72= Tb4b4b4{
Te6edf3mergeCommittedTb4b4b4.Te6edf3completeTb4b4b4(Tffa657UnitTb4b4b4)
Te6edf3releaseFinalizationTb4b4b4.Te6edf3awaitTb4b4b4(Tb4b4b4)
Tb4b4b4}

Tff7b72val Te6edf3associateJob Tff7b72= Te6edf3launch Tb4b4b4{ Te6edf3managerTb4b4b4.Te6edf3associateCurrentDeviceTb4b4b4(Te6edf3nodeNum Tff7b72= T79c0ff1T79c0ff2T79c0ff3Tb4b4b4, Te6edf3deviceId Tff7b72= Ta5d6ff"Ta5d6ffdeadbeefdeadbeefTa5d6ff"Tb4b4b4) Tb4b4b4}
Te6edf3mergeCommittedTb4b4b4.Te6edf3awaitTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3closeJob Tff7b72= Te6edf3launch Tb4b4b4{ Te6edf3managerTb4b4b4.Te6edf3closeTb4b4b4(Tb4b4b4) Tb4b4b4}
Te6edf3runCurrentTb4b4b4(Tb4b4b4)
Te6edf3assertFalseTb4b4b4(Te6edf3closeJobTb4b4b4.Te6edf3isCompletedTb4b4b4, Ta5d6ff"Ta5d6ffshutdown must wait for association finalizationTa5d6ff"Tb4b4b4)

Te6edf3releaseFinalizationTb4b4b4.Te6edf3completeTb4b4b4(Tffa657UnitTb4b4b4)
Te6edf3associateJobTb4b4b4.Te6edf3joinTb4b4b4(Tb4b4b4)
Te6edf3closeJobTb4b4b4.Te6edf3joinTb4b4b4(Tb4b4b4)

Tff7b72val Te6edf3retiredSource Tff7b72= Te6edf3buildDbNameTb4b4b4(Ta5d6ff"Ta5d6ffaddrBTa5d6ff"Tb4b4b4)
Te6edf3assertEqualsTb4b4b4(T79c0ff1Tb4b4b4, Te6edf3managerTb4b4b4.Te6edf3deletedDatabaseNamesTb4b4b4.Te6edf3count Tb4b4b4{ Tffa657it Tff7b72=Tff7b72= Te6edf3retiredSource Tb4b4b4}Tb4b4b4)
Te6edf3managerTb4b4b4.Te6edf3closeTb4b4b4(Tb4b4b4)
Te6edf3assertEqualsTb4b4b4(
T79c0ff1Tb4b4b4,
Te6edf3managerTb4b4b4.Te6edf3deletedDatabaseNamesTb4b4b4.Te6edf3count Tb4b4b4{ Tffa657it Tff7b72=Tff7b72= Te6edf3retiredSource Tb4b4b4}Tb4b4b4,
Ta5d6ff"Ta5d6ffsecond close must be idempotentTa5d6ff"Tb4b4b4,
Tb4b4b4)
Tb4b4b4}

T8b949e/**
* [DatabaseManager.close] must be idempotent after logical retirement: calling close twice must not throw. The
* first close physically cleans the logically-retired source; the second close is a safe no-op because the cache
* and retirement set are already empty.
*/
Tf0883e@Test
Tff7b72fun Td2a8ffcloseIsIdempotentAndCleansRetiredSourcesTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTestTb4b4b4(Te6edf3testDispatcherTb4b4b4) Tb4b4b4{
Te6edf3setupTwoDatabasesTb4b4b4(Tb4b4b4)
Te6edf3managerTb4b4b4.Te6edf3associateCurrentDeviceTb4b4b4(Te6edf3nodeNum Tff7b72= T79c0ff1T79c0ff2T79c0ff3Tb4b4b4, Te6edf3deviceId Tff7b72= Ta5d6ff"Ta5d6ffdeadbeefdeadbeefTa5d6ff"Tb4b4b4)

Te6edf3managerTb4b4b4.Te6edf3closeTb4b4b4(Tb4b4b4)
Te6edf3managerTb4b4b4.Te6edf3closeTb4b4b4(Tb4b4b4)
Tb4b4b4}

T8b949e/**
* When shutdown races an in-flight association that is paused before its merge commits, [close] must wait for the
* association to finalize (committing its retirement) before deciding ownership, then physically retire the merged
* source exactly once. This exercises the other side of
* [shutdownRacingMergeFinalizationDeletesRetiredSourceExactlyOnce], pausing at [TestDatabaseManager.beforeMerge]
* rather than after commit.
*/
Tf0883e@Test
Tff7b72fun Td2a8ffshutdownWaitsForPausedAssociationToFinalizeAndRetiresOnceTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTestTb4b4b4(Te6edf3testDispatcherTb4b4b4) Tb4b4b4{
Te6edf3setupTwoDatabasesTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3beforeMergeReached Tff7b72= Te6edf3CompletableDeferredTff7b72<Tffa657UnitTff7b72>Tb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3releaseMerge Tff7b72= Te6edf3CompletableDeferredTff7b72<Tffa657UnitTff7b72>Tb4b4b4(Tb4b4b4)
Te6edf3managerTb4b4b4.Te6edf3beforeMerge Tff7b72= Tb4b4b4{ Te6edf3_Tb4b4b4, Te6edf3_Tb4b4b4, Te6edf3_ Tff7b72-Tff7b72>
Te6edf3beforeMergeReachedTb4b4b4.Te6edf3completeTb4b4b4(Tffa657UnitTb4b4b4)
Te6edf3releaseMergeTb4b4b4.Te6edf3awaitTb4b4b4(Tb4b4b4)
Tb4b4b4}

Tff7b72val Te6edf3associateJob Tff7b72= Te6edf3launch Tb4b4b4{ Te6edf3managerTb4b4b4.Te6edf3associateCurrentDeviceTb4b4b4(Te6edf3nodeNum Tff7b72= T79c0ff1T79c0ff2T79c0ff3Tb4b4b4, Te6edf3deviceId Tff7b72= Ta5d6ff"Ta5d6ffdeadbeefdeadbeefTa5d6ff"Tb4b4b4) Tb4b4b4}
Te6edf3beforeMergeReachedTb4b4b4.Te6edf3awaitTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3closeJob Tff7b72= Te6edf3launch Tb4b4b4{ Te6edf3managerTb4b4b4.Te6edf3closeTb4b4b4(Tb4b4b4) Tb4b4b4}
Te6edf3runCurrentTb4b4b4(Tb4b4b4)
Te6edf3assertFalseTb4b4b4(Te6edf3closeJobTb4b4b4.Te6edf3isCompletedTb4b4b4, Ta5d6ff"Ta5d6ffshutdown must wait for association finalizationTa5d6ff"Tb4b4b4)

Te6edf3releaseMergeTb4b4b4.Te6edf3completeTb4b4b4(Tffa657UnitTb4b4b4)
Te6edf3associateJobTb4b4b4.Te6edf3joinTb4b4b4(Tb4b4b4)
Te6edf3closeJobTb4b4b4.Te6edf3joinTb4b4b4(Tb4b4b4)

Tff7b72val Te6edf3retiredSource Tff7b72= Te6edf3buildDbNameTb4b4b4(Ta5d6ff"Ta5d6ffaddrBTa5d6ff"Tb4b4b4)
Te6edf3assertEqualsTb4b4b4(T79c0ff1Tb4b4b4, Te6edf3managerTb4b4b4.Te6edf3deletedDatabaseNamesTb4b4b4.Te6edf3count Tb4b4b4{ Tffa657it Tff7b72=Tff7b72= Te6edf3retiredSource Tb4b4b4}Tb4b4b4)
Te6edf3managerTb4b4b4.Te6edf3closeTb4b4b4(Tb4b4b4)
Te6edf3assertEqualsTb4b4b4(
T79c0ff1Tb4b4b4,
Te6edf3managerTb4b4b4.Te6edf3deletedDatabaseNamesTb4b4b4.Te6edf3count Tb4b4b4{ Tffa657it Tff7b72=Tff7b72= Te6edf3retiredSource Tb4b4b4}Tb4b4b4,
Ta5d6ff"Ta5d6ffsecond close must be idempotentTa5d6ff"Tb4b4b4,
Tb4b4b4)
Tb4b4b4}

T8b949e/**
* When shutdown begins before an association has been admitted, the CLOSING lifecycle state must exclude that
* association: it cannot start, so no merge/retirement occurs and the active source keeps its ownership (it is
* closed but never physically retired as a merged-away DB).
*/
Tf0883e@Test
Tff7b72fun Td2a8ffshutdownRetainsOwnershipWhenAssociationIsBlockedFromStartingTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTestTb4b4b4(Te6edf3testDispatcherTb4b4b4) Tb4b4b4{
Te6edf3setupTwoDatabasesTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3releaseAssociate Tff7b72= Te6edf3CompletableDeferredTff7b72<Tffa657UnitTff7b72>Tb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3associationResult Tff7b72= Te6edf3CompletableDeferredTff7b72<Te6edf3ResultTff7b72<Tffa657UnitTff7b72>Tff7b72>Tb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3associateJob Tff7b72= Te6edf3launch Tb4b4b4{
Te6edf3releaseAssociateTb4b4b4.Te6edf3awaitTb4b4b4(Tb4b4b4)
Te6edf3associationResultTb4b4b4.Te6edf3completeTb4b4b4(
Te6edf3runCatching Tb4b4b4{ Te6edf3managerTb4b4b4.Te6edf3associateCurrentDeviceTb4b4b4(Te6edf3nodeNum Tff7b72= T79c0ff1T79c0ff2T79c0ff3Tb4b4b4, Te6edf3deviceId Tff7b72= Ta5d6ff"Ta5d6ffdeadbeefdeadbeefTa5d6ff"Tb4b4b4) Tb4b4b4}Tb4b4b4,
Tb4b4b4)
Tb4b4b4}

Tff7b72val Te6edf3closeJob Tff7b72= Te6edf3launch Tb4b4b4{ Te6edf3managerTb4b4b4.Te6edf3closeTb4b4b4(Tb4b4b4) Tb4b4b4}
Te6edf3closeJobTb4b4b4.Te6edf3joinTb4b4b4(Tb4b4b4)

Te6edf3releaseAssociateTb4b4b4.Te6edf3completeTb4b4b4(Tffa657UnitTb4b4b4)
Te6edf3associateJobTb4b4b4.Te6edf3joinTb4b4b4(Tb4b4b4)

Tff7b72val Te6edf3activeSource Tff7b72= Te6edf3buildDbNameTb4b4b4(Ta5d6ff"Ta5d6ffaddrBTa5d6ff"Tb4b4b4)
Te6edf3assertTrueTb4b4b4(
Te6edf3associationResultTb4b4b4.Te6edf3awaitTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3exceptionOrNullTb4b4b4(Tb4b4b4) Tff7b72is Te6edf3IllegalStateExceptionTb4b4b4,
Ta5d6ff"Ta5d6ffthe association must be rejected after shutdown beginsTa5d6ff"Tb4b4b4,
Tb4b4b4)
Te6edf3assertTrueTb4b4b4(Te6edf3activeSource Tff7b72!in Te6edf3armableDsTb4b4b4.Te6edf3dataTb4b4b4.Te6edf3firstTb4b4b4(Tb4b4b4)Tff7b72[Te6edf3retiredDbNamesKeyTff7b72]Tb4b4b4.Te6edf3orEmptyTb4b4b4(Tb4b4b4)Tb4b4b4)
Te6edf3assertEqualsTb4b4b4(
T79c0ff0Tb4b4b4,
Te6edf3managerTb4b4b4.Te6edf3deletedDatabaseNamesTb4b4b4.Te6edf3count Tb4b4b4{ Tffa657it Tff7b72=Tff7b72= Te6edf3activeSource Tb4b4b4}Tb4b4b4,
Ta5d6ff"Ta5d6ffa blocked (never-admitted) association must not retire the active sourceTa5d6ff"Tb4b4b4,
Tb4b4b4)
Tb4b4b4}
Tb4b4b4}

Served by rngit 1.5.0 - Generated in 0.13s